package com.cskaoyan.javase.oop1._6constructor;

/**
 * 创建一个教师类，有课程和年龄两个属性，行为是可以上课。
 * 现在我们需要创建以下对象：
 * 18岁的Java老师对象
 * 28岁的C++老师对象
 * 30岁的Python老师对象
 * ...
 *
 * 以往我们创建对象后,必须手动对象名点给成员变量赋值,这种方式多多少少有些复杂
 * 那么我们思考: 有没有一种方式,能够在创建对象过程中,在对象"出厂"之前就能够随意赋值呢?
 *
 * 显然我们目前已经学习的知识是不够用了,需要新的知识,这就是构造器
 *
 * 构造器,也叫构造方法,它是一种比较特殊的方法,它的语法是:
 *      [访问权限修饰符] 类名(形参列表){
 *          //方法体
 *      }
 * 逐一解释语法:
 *      1.访问权限修饰符我们这里没有学,默认是public.当然访问权限修饰符可以不写.
 *      2.构造方法是没有返回值类型,当然也没有返回值,不需要返回值类型.
 *      3.构造器的名字应该保持和类名完全一致,由于类名是大驼峰式的,所以构造器也是
 *      4.形参列表和方法体和一般方法没有区别
 *
 * 构造方法很特殊,它特殊在:
 *      1.构造方法虽然是方法,但是它不是程序员去调用的,程序员没有任何手段直接去调用构造方法.
 *          构造方法是给JVM调用的,它的作用是给成员变量赋值用的
 *      2.构造器虽然程序员不能直接调用,但可以指示JVM去调用哪个构造器来给成员变量赋值
 *          语法: _1new 类名/构造器名(实参)
 *          通过传入不同的实参,可以去选择调用不同的构造器
 *          类中的构造器完全可能不止一个,可以有多个,它们构成方法重载的关系.
 *      所以,在java中,使用new关键字创建对象,就必然会让JVM在创建对象时调用构造方法.
 *
 * 构造方法的使用:
 *      1.一个类中,如果没有直接提供任何构造器,那么编译器在编译代码后,会自动提供一个默认无参构造,简称无参构造,它的格式是:
 *      public 类名(){}
 *      2.无参构造无法完成成员变量的赋值,我们需要有参构造完成赋值.
 *      它的格式是:
 *      [访问权限修饰符] 类名(形参列表){
 *          this.成员变量名 = 形参名;
 *          ...
 *      }
 *      3.构造器是有标注格式的,有参构造是
 *      [访问权限修饰符] 类名(形参列表){
 *          this.成员变量名 = 形参名;
 *          ...
 *      }
 *      无参构造:
 *      [访问权限修饰符] 类名(){}
 *
 *      使用标准格式有参构造器赋值,它的赋值一定是准确的,一定是可以相信,一定是不会出现问题的.
 *      在java当中,在创建对象的过程中,给成员变量的赋值手段很多,但不管有多少,构造器的赋值永远是最后进行的
 *      你永远可以相信构造器
 *
 *      4.标准格式的构造器,格式是固定的.就不需要手动写代码.IDEA提供了快捷键,自动生成构造方法
 *      快捷键alt + insert
 *
 *      5.如果类中显式提供了任何构造器，就没有默认无参构造了。我们建议，如果一个类需要被创建对象，那么建议手动提供默认无参，这是一个好习惯。
 *
 * 构造器的使用细节：
 *      1.和成员方法一样，构造器中也能够去使用this关键字，表示当前对象。所以你可以在构造器中去调用成员方法和访问成员变量。
 *      但是我们建议，构造器就是为成员变量赋值用的，不要去给它提供别的用途。
 *      如果对象的创建，一定要需要依赖于很复杂的业务代码逻辑，建议提取出方法增强代码可读性。
 *      2.类中的多个构造方法构成方法重载，切记形参列表不同。
 *      3.同类的构造器可以互相调用。
 *      语法：
 *      this(实参);
 *      根据实参的类型 个数不同决定调用哪个构造器。
 *      但是要注意，当this表示调用其它构造器时，该行代码必须在构造器的第一行。（原因挖个坑，后面填）
 *
 *  思考:
 *      比如我new对象创建了三个对象,
 *          构造器执行了几次? 3 每new一个对象,都会调用一次构造器
 *          类加载了几次? 但是类加载只有一次
 *
 * @since 15:42
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        // 调用的是Teacher的无参构造方法
        /*Teacher t1 = _1new Teacher();
        t1.course = "JavaSE";
        t1.age = 18;
        Teacher t2 = _1new Teacher();
        t2.course = "C++";
        t2.age = 28;
        Teacher t3 = _1new Teacher();
        t3.course = "Python";
        t3.age = 38;*/

        Teacher t = new Teacher("JavaSE", 18);
        // override.age = 28;
        System.out.println(t.age);
        System.out.println(t.course);

        Teacher t2 = new Teacher("数学", 48, "张三");
        System.out.println(t2.course);
        System.out.println(t2.age);
        System.out.println(t2.name);

    }
}

class Teacher {
    String course = "xxx";
    int age = 100;
    String name = "yyy";

    public void teach() {
        System.out.println("年龄为" + age + "岁的" + course + "老师，正在上课！");
    }

    // 定义一个有参构造给course和age同时赋值
    public Teacher(String course, int age) {
        this.course = course;
        this.age = age;
    }

    // 定义一个有参构造给age单独赋值
    public Teacher(int age) {
        this.age = age;
    }

    // 定义一个有参构造给course单独赋值
    public Teacher(String course) {
        this.course = course;
    }

    /*public Teacher(String name) {
        this.name = name;
    }*/

    // 默认无参
    public Teacher() {
    }

    // 需求：写一个构造器，完成三个成员变量的赋值
    public Teacher(String course, int age, String name) {
        // 如果在这里我能去调用同类中的双参构造，那么就可以节省一些代码
        // this.course = course;
        // this.age = age;
        // Call to 'this()' must be first statement in constructor body
        this(course, age);
        this.name = name;
    }
}